
#include <iostream>
#include <sstream>
#include <vector>

#include <core/Logging.h>
#include <WorkUnit/WorkUnit.h>
#include <Dispatcher/Dispatcher.h>
#include <Processing/LocalImmediateProcessingUnit.h>
#include <Processing/ThreadedLocalProcessor.h>
#include <WorkUnit/SimpleParameter.h>
#include <RemoteMonitor/WorkUnitMonitor.h>

using namespace std;
using namespace ComputingObjects;


template class SimpleParameter<std::list<int> >;
typedef class SimpleParameter<std::list<int> > TestParams;

class TestWU : public WorkUnit
{
public:
	TestParams myParams;
	IntParam myResult;

	TestWU(std::string id, TestParams& params)
		: WorkUnit(id, params), myParams(params), myResult(0)
	{}

	void compute()
	{
		setState(executing);
		std::list<int>::iterator anIT = myParams.getParam().begin();
		while (anIT != myParams.getParam().end()){
			myResult.getParam() += *anIT;
			++anIT;
		}
		setState(completed);
	}

	IntParam& getResult()
	{
		return myResult;
	}
};

class ProcessAllResults : public WorkUnit, public WorkUnitObserver {
public:
	ProcessAllResults(UintParam param)
		: WorkUnit("ProcessAllResults", param), myParam(param), myResult(0)
	{
	}

	virtual ~ProcessAllResults() 
	{
	}

	void stateChange(WorkUnit& unit, enum WorkUnit::state previousState)
	{
		// Can I eliminate this cast?
		TestWU& wu((TestWU&)unit);
		if (unit.getState() == completed) {
			myInputs.push_back(wu.myResult.getParam());
		}
	}
	void compute()
	{
		setState(executing);
		if ( myParam.getParam() == myInputs.size()) {
			// Sum results
			vector<int>::iterator anIT = myInputs.begin();
			while (anIT != myInputs.end()) {
				myResult.getParam() += *anIT;
				++anIT;
			}
			setState(completed);
		}
	}
	IntParam& getResult()
	{
		return myResult;
	}

	UintParam myParam;
	vector<int> myInputs;
	IntParam myResult;
};


class TestDispatcher : public Dispatcher
{
public:
	TestDispatcher(ProcessingUnit& processor)
		: Dispatcher(processor)
	{}
	
	void execute()
	{
		while (myProcessor.canAcceptWork() && !myDispatchQueue.empty()) {
			SharedWorkUnit unit(myDispatchQueue.front());
			myDispatchQueue.pop_front();
			dispatch(unit);
			notifyDispatch(unit);
		}
			
	}
	
	void dispatch(SharedWorkUnit aUnit)
	{
		aUnit->setState(WorkUnit::dispatched);
		myProcessor.execute(aUnit);
	}
};


static const unsigned int numInts = 50;
static const unsigned int numWorkUnits = 10;

#define TEST_LOG() \
	if (1) ; \
	else std::cout

int main()
{
	int check = 0;
	TEST_LOG()<<"Roy"<<std::endl;
	srand(time(NULL));
	//LocalImmediateProcessingUnit theProcessor;
	ThreadedLocalProcessor theProcessor;
	TestDispatcher disp(theProcessor);
	SharedWorkUnitObserver rmon (new WorkUnitMonitor("127.0.0.1", 1234));

	ProcessAllResults* summerPtr;
	UintParam inParm(numWorkUnits);
	SharedWorkUnit summer(summerPtr = new ProcessAllResults(inParm));
	SharedWorkUnitObserver bad(summerPtr);
	summer->attach(rmon);
	for (unsigned int j=0; j< numWorkUnits; j++) {
		TestParams params;
		for (unsigned int i=0; i< numInts; i++)
		{
			int val = rand() % 1000;
			check += val; 
			params.getParam().push_back(val);
		}
		std::stringstream id;
		id<<"WorkUnit("<<j<<")";
		SharedWorkUnit obj(new TestWU (id.str(), params));
		obj->attach(bad);
		obj->attach(rmon);
		disp.addWorkUnit(obj);
	}
	disp.addWorkUnit(summer);
	disp.execute();
	while (summerPtr->getState() != WorkUnit::completed)
		;
	cout<<"Total Result is "<<summerPtr->getResult().getParam()<<" expected "<<check<<endl;
	return 0;	
}
